
/*
 *  目的： 研究 printf("%d\r\n",12345); 如何实现的
 根据printf的原理，实现一个在裸机可以使用的printf函数

 *
 *  测试平台:   ubuntu16.04(64位机器)  gcc -m32 -o 02_format.out  02_format.c 
 *				ubuntu9.10 (32位机器)  gcc      -o 02_format  02_format.c.c 
 *  编译器  :   gcc

 可变参数一个用途： 

   int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);
  有2个函数open
  声明其实是：
  int open(const char *pathname, int flags, ...);

 */
 
#include <stdio.h>
#include <stdarg.h>
 
//在x86平台下,结构体默认按照4个字节对齐
struct  person{
	char *name;  //4个字节占用
	int  age;   // 4个
	char score;  // 空3个字节
	int  id;     // 4个字节
};

//按照1字节对齐
struct  person1{
	char *name;
	int  age;
	char score;
	int  id;
}__attribute__ ((packed));
//aligned (n) 按照n字节对齐
struct  person2{
	char *name;
	int  age;
	char score;
	int  id;
}__attribute((aligned (4)));


//第一种：可变参数实现2种方式

/* 
 *int printf(const char *format, ...); 
 *依据:x86平台,函数调用时参数传递是使用堆栈来实现的 
 *目的:将所有传入的参数全部打印出来 
 */ 
int push_test1(const char *format, ...)
{
	char *p = (char *)&format;
	int i;
	struct  person per;  
	char c;
	double d;
	
	printf("arg1 : %s\n",format);	  // arg1 : abcd   
	p = p + sizeof(char *);
	

	i = *((int *)p);
	p = p + sizeof(int); 	
	printf("arg2 : %d\n",i);     // arg2 : 123 
 

 	per = *((struct  person *)p); 
	p = p + sizeof(struct person);	
	//arg3: .name = www.100ask.org, .age = 10, .socre=A  .id=123
	printf("arg3: .name = %s, .age = %d, .socre=%c  .id=%d\n",\
		          per.name,   per.age,   per.score, per.id);   
 
  

	c = *((char *)p);
	// +3为什么，这里也是按照4字节对齐
	p = p + ((sizeof(char) + 3) & ~3);	
	printf("arg4: %c\n",c);    // arg4: c 
// 注意，这里必须移动4个字节， 32位系统4个字节对齐，空出来3个就空了
 
  
//传入小数自动转化为double
	d = *((double *)p);
	p = p + sizeof(double);
	 // arg5: 2.790000
	printf("arg5: %f\n",d);
	
	return 0;
}





/* 
 *  第二种，访问可变参数方式，使用 宏系统提供
 va_list p;
va_start(p, format ); // 移动指针指向到了第一个可变参数，首地址
i = va_arg(p,int); // 取值，移动指针到下一个值首地址
d = va_arg(p,double); // 取值，移动到下一个值首地址
va_arg(p,int); // 注意： 32位系统是4个字节对齐，所以 这里 是int 不是char 读取的是char
                      传递float ,按照double 来处理                         

   上面几个宏，原理就是push_test1

    问题： 必须要知道传入参数类型
 */ 
int push_test2(const char *format, ...)
{
	//char *p = (char *)&format;
	int i;
	struct  person per;  
	char c;
	double d;
	va_list p;
	
	printf("arg1 : %s\n",format);	 
 
    //==============
	//p = p + sizeof(char *);
	va_start(p, format );   
	
	/*指针对连续空间操作时: 1) 取值  2)移动指针*/  
	//i = *((int *)p);
	//p = p + sizeof(int); 	
	i = va_arg(p,int);
	printf("arg2 : %d\n",i);   
 
    //==============             
	/*指针对连续空间操作时: 1) 取值  2)移动指针*/    
 	//per = *((struct  person *)p); 
	//p = p + sizeof(struct person);	
	per = va_arg(p,struct person);
	printf("arg3: .name = %s, .age = %d, .socre=%c  .id=%d\n",\
		          per.name,   per.age,   per.score, per.id);   
 
    //==============    
	/*指针对连续空间操作时: 1) 取值  2)移动指针*/
	//c = *((char *)p);
	//p = p + ((sizeof(char) + 3) & ~3);	
	c = va_arg(p,int);   // 注意： 32位系统是4个字节对齐，所以 这里 是int 不是char  
	printf("arg4: %c\n",c);
 
    //==============    
	/*指针对连续空间操作时: 1) 取值  2)移动指针*/
	//d = *((double *)p);
	//p = p + sizeof(double);
	d = va_arg(p,double);	
	/*避免"野指针"*/
	//p = (char *)0;
	va_end( p ); 
	printf("arg5: %f\n",d);
	
	return 0;
}


 
int main(int argc,char **argv)
{
	struct  person per={"www.100ask.org",10,'A',123};
	


	//打印地址
	printf("&per.name  =%p,per.name  =%s\n",&per.name ,per.name);   //0xffab8c20
	printf("&per.age   =%p,per.age   =%d\n",&per.age  ,per.age);	 // 0xffab8c24
	printf("&per.score =%p,per.score =%d\n",&per.score,per.score);	// 	0xffab8c28
	printf("&per.id    =%p,per.id    =%d\n",&per.id   ,per.id);		//  0xffab8c2c


    printf("struct  person= %u\r\n",sizeof(struct  person));   // 16

	struct  person1 per1={};
	printf("struct  person1= %u\r\n",sizeof(struct  person1));   // 13
	
    struct  person2 per2={};
    printf("struct  person2= %u\r\n",sizeof(struct  person2));   // 13
  
  	 
    push_test1("abcd",123,per,'c',2.79); 

	printf("***************************************");
    
	push_test2("abcd",123,per,'c',2.79); 
	
	 		
	return 0;
}	

